package p01;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Arrays;
import java.util.Locale;
import java.util.ResourceBundle;

/**
 * ApplicationContext接口是BeanFactory子接口，所以天然具有BeanFactory的功能
 * 同时该接口还继承了其他接口，因此该接口扩展了额外功能
 * 四个额外父接口：
 * 1.MessageSource: 处理国际化资源的能力
 * 2.ResourcePatternResolver: 利用通配符匹配资源(硬盘上的文件)的能力
 * 3.ApplicationEventPublisher: 事件发布能力
 * 4.EnvironmentCapable: 处理Spring的环境信息的能力
 * <p>
 * ApplicationContext不仅是继承了BeanFactory接口，也组合了BeanFactory并扩展(继承其他接口)了功能
 *
 * @author ：chen weishuai
 */
@Slf4j
@SpringBootApplication
public class ApplicationContextDemo {
    public static void main(String[] args) throws IOException {
        // 演示 ApplicationContext 接口的扩展功能
        ConfigurableApplicationContext context = SpringApplication.run(ApplicationContextDemo.class);

        // 使用 ResourceBundle 直接加载 messages_zh_CN
        ResourceBundle resourceBundle = ResourceBundle.getBundle("messages", Locale.CHINA);
        System.out.println("Available keys: " + Arrays.asList(resourceBundle.getKeys()));
        System.out.println("Current locale: " + Locale.CHINA);

        // 1.国际化
        log.info(context.getMessage("hi", null, Locale.US));
        log.info(context.getMessage("hi", null, Locale.CHINA));
        log.info(context.getMessage("hi", null, Locale.getDefault()));

        // 2.根据通配符获取资源，classpath:xxx(当前类路径下找到指定资源),classpath*:xxx(类路径下找到所有指定资源),file:xxx(磁盘上找资源)
        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {
            log.info("资源 {}", resource);
        }

        // 3.获取环境变量信息,既可以获取操作系统的环境变量，又可以获取配置文件中的变量
        String javaHome = context.getEnvironment().getProperty("JAVA_HOME");
        log.info("java_home:{}", javaHome);
        String personName = context.getEnvironment().getProperty("person.name");
        log.info("person.name:{}", personName);

        /**
         * 4.发布事件
         *  发布事件需要有事件对象，ApplicationEvent子类
         *  监听器监听该事件，容器中的任何bean都可以作为监听器,监听方法需要EventListener注解
         *  其他地方可以直接注入ApplicationEventPublisher获取容器对象
         *  事件发布于监听的原理最后两章会介绍
         */
        log.info("发布事件");
        context.publishEvent(new MyEvent(context));


        int exit = SpringApplication.exit(context, () -> 0);
    }

    /**
     * 自定义事件
     */
    static class MyEvent extends ApplicationEvent {
        /**
         * @param source 事件源，谁发的事件
         */
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    static class MyListener {
        /**
         * 监听MyEvent类型的事件
         *
         * @param event 监听的事件类型
         */
        @EventListener
        public void listen(MyEvent event) {
            log.info("监听到了事件发生...:{}", event);
        }
    }
}
